//
//  ZFCrypto.m
//  ZFCommonCore
//
//  Created by 赵梁 on 2016/10/2.
//  Copyright © 2016年 zhaoliang. All rights reserved.
//

#import "ZFCryptor.h"


#import <CommonCrypto/CommonCryptor.h>
#import <GTMBase64/GTMBase64.h>
#import "HexStringUtil.h"

@implementation ZFCryptor

+ (NSString*)cryptStrByBase64:(NSString*)plZFnText tripleKey:(NSString *)desKey algorithm:(ZFCryptAlgorithm)algorithm operation:(ZFCryptOperation)operation
{
    NSData *dataTemp = [plZFnText dataUsingEncoding:NSUTF8StringEncoding];
    if (operation == kCCDecrypt) {
        dataTemp = [GTMBase64 decodeData:dataTemp];
    } else {
        dataTemp = [GTMBase64 encodeData:dataTemp];
    }
    
    NSData *returnData = [self cryptData:dataTemp tripleKey:desKey algorithm:algorithm operation:operation];
    
    if (returnData) {
        if (operation == ZFCryptDecrypt) {
            returnData = [GTMBase64 decodeData:returnData];
        } else {
            returnData = [GTMBase64 encodeData:returnData];
        }
        if (returnData) {
            NSString *returnStr = [[NSString alloc] initWithData:returnData encoding:NSUTF8StringEncoding];
            return returnStr;
        }
    }
    return nil;
}

+ (NSString*)cryptStrByHex:(NSString*)plZFnText tripleKey:(NSString *)desKey algorithm:(ZFCryptAlgorithm)algorithm operation:(ZFCryptOperation)operation
{
    NSData *dataTemp = [HexStringUtil dataFromHexString:plZFnText];
    
    NSData *returnData = [self cryptData:dataTemp tripleKey:desKey algorithm:algorithm operation:operation];
    
    if (returnData) {
        NSString *returnStr = [HexStringUtil hexStringFromData:returnData];
        return returnStr;
    }
    return nil;
}

#pragma mark - core helper

+ (NSString*)cryptStr:(NSString*)plZFnText tripleKey:(NSString *)desKey algorithm:(ZFCryptAlgorithm)algorithm operation:(ZFCryptOperation)operation
{
    NSData *plZFnData = [plZFnText dataUsingEncoding:NSUTF8StringEncoding];
    NSData *returnData = [self cryptData:plZFnData tripleKey:desKey algorithm:algorithm operation:operation];
    return [[NSString alloc] initWithData:returnData encoding:NSUTF8StringEncoding];
}

+ (NSData*)cryptData:(NSData*)plZFnData tripleKey:(NSString *)desKey algorithm:(ZFCryptAlgorithm)algorithm operation:(ZFCryptOperation)operation
{
    if (![plZFnData isKindOfClass: [NSData class]]) {
        return nil;
    }
    
    CCAlgorithm ccalgorithm;
    NSInteger ccblockSize;
    NSInteger kCCKeySize;
    switch (algorithm) {
        case ZFCryptAlgorithmAES128:
        {
            ccalgorithm = kCCAlgorithmAES;
            ccblockSize = kCCBlockSizeAES128;
            kCCKeySize = kCCKeySizeAES128;
            break;
        }
        case ZFCryptAlgorithmAES256:
        {
            ccalgorithm = kCCAlgorithmAES;
            ccblockSize = kCCBlockSizeAES128;
            kCCKeySize = kCCKeySizeAES256;
            break;
        }
        case ZFCryptAlgorithmDES:
        {
            ccalgorithm = kCCAlgorithmDES;
            ccblockSize = kCCBlockSizeDES;
            kCCKeySize = kCCKeySizeDES;
            break;
        }
        case ZFCryptAlgorithm3DES:
        {
            ccalgorithm = kCCAlgorithm3DES;
            ccblockSize = kCCBlockSize3DES;
            kCCKeySize = kCCKeySize3DES;
            break;
        }
            
        default:
            break;
    }
    
    size_t plZFnTextBufferSize = [plZFnData length];
    const void *vplZFnText = (const void *)[plZFnData bytes];
    
    size_t bufferPtrSize = (plZFnTextBufferSize + ccblockSize) & ~(ccblockSize - 1);
    uint8_t *bufferPtr = malloc( bufferPtrSize * sizeof(uint8_t));
    size_t movedBytes = 0;
    memset((void *)bufferPtr, 0x0, bufferPtrSize);
    
    // 偏移量
    // NSString *initVec = @"init Vec";
    // const void *vinitVec = (const void *) [initVec UTF8String];
    // Byte iv[] = {0x12, 0x34, 0x56, 0x78, 0x90, 0xAB, 0xCD, 0xEF};
    
    CCOperation op = kCCEncrypt;
    if (operation==ZFCryptDecrypt) {
        op = kCCDecrypt;
    }
    CCCryptorStatus ccStatus = CCCrypt(op,
                       ccalgorithm,
                       kCCOptionPKCS7Padding | kCCOptionECBMode,
                       [desKey UTF8String],
                       kCCKeySize,
                       nil,
                       vplZFnText,
                       plZFnTextBufferSize,
                       (void *)bufferPtr,
                       bufferPtrSize,
                       &movedBytes);
    
    if (ccStatus != kCCSuccess) {
        if (ccStatus == kCCParamError) NSLog(@"PARAM ERROR");
        else if (ccStatus == kCCBufferTooSmall) NSLog(@"BUFFER TOO SMALL");
        else if (ccStatus == kCCMemoryFailure) NSLog(@"MEMORY FZFLURE");
        else if (ccStatus == kCCAlignmentError) NSLog(@"ALIGNMENT");
        else if (ccStatus == kCCDecodeError) NSLog(@"DECODE ERROR");
        else if (ccStatus == kCCUnimplemented) NSLog(@"UNIMPLEMENTED");
        else NSLog(@"unkown error");
        free(bufferPtr);
        return nil;
    }
    NSData *returnData = [NSData dataWithBytes:(const void *)bufferPtr length:(NSUInteger)movedBytes];
    free(bufferPtr);
    return returnData;
}

@end
